home *** CD-ROM | disk | FTP | other *** search
- package Win32::ODBC;
-
- $VERSION = '0.03';
-
- # Win32::ODBC.pm
- # +==========================================================+
- # | |
- # | ODBC.PM package |
- # | --------------- |
- # | |
- # | Copyright (c) 1996, 1997 Dave Roth. All rights reserved. |
- # | This program is free software; you can redistribute |
- # | it and/or modify it under the same terms as Perl itself. |
- # | |
- # +==========================================================+
- #
- #
- # based on original code by Dan DeMaggio (dmag@umich.edu)
- #
- # Use under GNU General Public License or Larry Wall's "Artistic License"
- #
- # Check the README.TXT file that comes with this package for details about
- # it's history.
- #
-
- require Exporter;
- require DynaLoader;
-
- $ODBCPackage = "Win32::ODBC";
- $ODBCPackage::Version = 970208;
- $::ODBC = $ODBCPackage;
- $CacheConnection = 0;
-
- # Reserve ODBC in the main namespace for US!
- *ODBC::=\%Win32::ODBC::;
-
-
- @ISA= qw( Exporter DynaLoader );
- # Items to export into callers namespace by default. Note: do not export
- # names by default without a very good reason. Use EXPORT_OK instead.
- # Do not simply export all your public functions/methods/constants.
- @EXPORT = qw(
- ODBC_ADD_DSN
- ODBC_REMOVE_DSN
- ODBC_CONFIG_DSN
-
- SQL_DONT_CLOSE
- SQL_DROP
- SQL_CLOSE
- SQL_UNBIND
- SQL_RESET_PARAMS
-
- SQL_FETCH_NEXT
- SQL_FETCH_FIRST
- SQL_FETCH_LAST
- SQL_FETCH_PRIOR
- SQL_FETCH_ABSOLUTE
- SQL_FETCH_RELATIVE
- SQL_FETCH_BOOKMARK
-
- SQL_COLUMN_COUNT
- SQL_COLUMN_NAME
- SQL_COLUMN_TYPE
- SQL_COLUMN_LENGTH
- SQL_COLUMN_PRECISION
- SQL_COLUMN_SCALE
- SQL_COLUMN_DISPLAY_SIZE
- SQL_COLUMN_NULLABLE
- SQL_COLUMN_UNSIGNED
- SQL_COLUMN_MONEY
- SQL_COLUMN_UPDATABLE
- SQL_COLUMN_AUTO_INCREMENT
- SQL_COLUMN_CASE_SENSITIVE
- SQL_COLUMN_SEARCHABLE
- SQL_COLUMN_TYPE_NAME
- SQL_COLUMN_TABLE_NAME
- SQL_COLUMN_OWNER_NAME
- SQL_COLUMN_QUALIFIER_NAME
- SQL_COLUMN_LABEL
- SQL_COLATT_OPT_MAX
- SQL_COLUMN_DRIVER_START
- SQL_COLATT_OPT_MIN
- SQL_ATTR_READONLY
- SQL_ATTR_WRITE
- SQL_ATTR_READWRITE_UNKNOWN
- SQL_UNSEARCHABLE
- SQL_LIKE_ONLY
- SQL_ALL_EXCEPT_LIKE
- SQL_SEARCHABLE
- );
- #The above are included for backward compatibility
-
-
- sub new
- {
- my ($n, $self);
- my ($type) = shift;
- my ($DSN) = shift;
- my (@Results) = @_;
-
- if (ref $DSN){
- @Results = ODBCClone($DSN->{'connection'});
- }else{
- @Results = ODBCConnect($DSN, @Results);
- }
- @Results = processError(-1, @Results);
- if (! scalar(@Results)){
- return undef;
- }
- $self = bless {};
- $self->{'connection'} = $Results[0];
- $ErrConn = $Results[0];
- $ErrText = $Results[1];
- $ErrNum = 0;
- $self->{'DSN'} = $DSN;
- $self;
- }
-
- ####
- # Close this ODBC session (or all sessions)
- ####
- sub Close
- {
- my ($self, $Result) = shift;
- $Result = DESTROY($self);
- $self->{'connection'} = -1;
- return $Result;
- }
-
- ####
- # Auto-Kill an instance of this module
- ####
- sub DESTROY
- {
- my ($self) = shift;
- my (@Results) = (0);
- if($self->{'connection'} > -1){
- @Results = ODBCDisconnect($self->{'connection'});
- @Results = processError($self, @Results);
- if ($Results[0]){
- undef $self->{'DSN'};
- undef @{$self->{'fnames'}};
- undef %{$self->{'field'}};
- undef %{$self->{'connection'}};
- }
- }
- return $Results[0];
- }
-
-
- sub sql{
- return (Sql(@_));
- }
-
- ####
- # Submit an SQL Execute statement for processing
- ####
- sub Sql{
- my ($self, $Sql, @Results) = @_;
- @Results = ODBCExecute($self->{'connection'}, $Sql);
- return updateResults($self, @Results);
- }
-
- ####
- # Retrieve data from a particular field
- ####
- sub Data{
-
- # Change by JOC 06-APR-96
- # Altered by Dave Roth <dave@roth.net> 96.05.07
- my($self) = shift;
- my(@Fields) = @_;
- my(@Results, $Results, $Field);
-
- if ($self->{'Dirty'}){
- GetData($self);
- $self->{'Dirty'} = 0;
- }
- @Fields = @{$self->{'fnames'}} if (! scalar(@Fields));
- foreach $Field (@Fields) {
- if (wantarray) {
- push(@Results, data($self, $Field));
- } else {
- $Results .= data($self, $Field);
- }
- }
- return wantarray ? @Results : $Results;
- }
-
- sub DataHash{
- my($self, @Results) = @_;
- my(%Results, $Element);
-
- if ($self->{'Dirty'}){
- GetData($self);
- $self->{'Dirty'} = 0;
- }
- @Results = @{$self->{'fnames'}} if (! scalar(@Results));
- foreach $Element (@Results) {
- $Results{$Element} = data($self, $Element);
- }
-
- return %Results;
- }
-
- ####
- # Retrieve data from the data buffer
- ####
- sub data
- { $_[0]->{'data'}->{$_[1]}; }
-
-
- sub fetchrow{
- return (FetchRow(@_));
- }
- ####
- # Put a row from an ODBC data set into data buffer
- ####
- sub FetchRow{
- my ($self, @Results) = @_;
- my ($item, $num, $sqlcode);
- # Added by JOC 06-APR-96
- # $num = 0;
- $num = 0;
- undef $self->{'data'};
-
-
- @Results = ODBCFetch($self->{'connection'}, @Results);
- if (! (@Results = processError($self, @Results))){
- ####
- # There should be an innocuous error "No records remain"
- # This indicates no more records in the dataset
- ####
- return undef;
- }
- # Set the Dirty bit so we will go and extract data via the
- # ODBCGetData function. Otherwise use the cache.
- $self->{'Dirty'} = 1;
-
- # Return the array of field Results.
- return @Results;
- }
-
- sub GetData{
- my($self) = @_;
- my(@Results, $num);
-
- @Results = ODBCGetData($self->{'connection'});
- if (!(@Results = processError($self, @Results))){
- return undef;
- }
- ####
- # This is a special case. Do not call processResults
- ####
- ClearError();
- foreach (@Results){
- s/ +$//; # HACK
- $self->{'data'}->{ ${$self->{'fnames'}}[$num] } = $_;
- $num++;
- }
- # return is a hack to interface with a assoc array.
- return wantarray? (1, 1): 1;
- }
-
- ####
- # See if any more ODBC Results Sets
- # Added by Brian Dunfordshore <Brian_Dunfordshore@bridge.com>
- # 96.07.10
- ####
- sub MoreResults{
- my ($self) = @_;
-
- my(@Results) = ODBCMoreResults($self->{'connection'});
- return (processError($self, @Results))[0];
- }
-
- ####
- # Retrieve the catalog from the current DSN
- # NOTE: All Field names are uppercase!!!
- ####
- sub Catalog{
- my ($self) = shift;
- my ($Qualifier, $Owner, $Name, $Type) = @_;
- my (@Results) = ODBCTableList($self->{'connection'}, $Qualifier, $Owner, $Name, $Type);
-
- # If there was an error return 0 else 1
- return (updateResults($self, @Results) != 1);
- }
-
- ####
- # Return an array of names from the catalog for the current DSN
- # TableList($Qualifier, $Owner, $Name, $Type)
- # Return: (array of names of tables)
- # NOTE: All Field names are uppercase!!!
- ####
- sub TableList{
- my ($self) = shift;
- my (@Results) = @_;
- if (! scalar(@Results)){
- @Results = ("", "", "%", "TABLE");
- }
-
- if (! Catalog($self, @Results)){
- return undef;
- }
- undef @Results;
- while (FetchRow($self)){
- push(@Results, Data($self, "TABLE_NAME"));
- }
- return sort(@Results);
- }
-
-
- sub fieldnames{
- return (FieldNames(@_));
- }
- ####
- # Return an array of fieldnames extracted from the current dataset
- ####
- sub FieldNames { $self = shift; return @{$self->{'fnames'}}; }
-
-
- ####
- # Closes this connection. This is used mostly for testing. You should
- # probably use Close().
- ####
- sub ShutDown{
- my($self) = @_;
- print "\nClosing connection $self->{'connection'}...";
- $self->Close();
- print "\nDone\n";
- }
-
- ####
- # Return this connection number
- ####
- sub Connection{
- my($self) = @_;
- return $self->{'connection'};
- }
-
- ####
- # Returns the current connections that are in use.
- ####
- sub GetConnections{
- return ODBCGetConnections();
- }
-
- ####
- # Set the Max Buffer Size for this connection. This determines just how much
- # ram can be allocated when a fetch() is performed that requires a HUGE amount
- # of memory. The default max is 10k and the absolute max is 100k.
- # This will probably never be used but I put it in because I noticed a fetch()
- # of a MEMO field in an Access table was something like 4Gig. Maybe I did
- # something wrong, but after checking several times I decided to impliment
- # this limit thingie.
- ####
- sub SetMaxBufSize{
- my($self, $Size) = @_;
- my(@Results) = ODBCSetMaxBufSize($self->{'connection'}, $Size);
- return (processError($self, @Results))[0];
- }
-
- ####
- # Returns the Max Buffer Size for this connection. See SetMaxBufSize().
- ####
- sub GetMaxBufSize{
- my($self) = @_;
- my(@Results) = ODBCGetMaxBufSize($self->{'connection'});
- return (processError($self, @Results))[0];
- }
-
-
- ####
- # Returns the DSN for this connection as an associative array.
- ####
- sub GetDSN{
- my($self, $DSN) = @_;
- if(! ref($self)){
- $DSN = $self;
- $self = 0;
- }
- if (! $DSN){
- $self = $self->{'connection'};
- }
- my(@Results) = ODBCGetDSN($self, $DSN);
- return (processError($self, @Results));
- }
-
- ####
- # Returns an associative array of $XXX{'DSN'}=Description
- ####
- sub DataSources{
- my($self, $DSN) = @_;
- if(! ref $self){
- $DSN = $self;
- $self = 0;
- }
- my(@Results) = ODBCDataSources($DSN);
- return (processError($self, @Results));
- }
-
- ####
- # Returns an associative array of $XXX{'Driver Name'}=Driver Attributes
- ####
- sub Drivers{
- my($self) = @_;
- if(! ref $self){
- $self = 0;
- }
- my(@Results) = ODBCDrivers();
- return (processError($self, @Results));
- }
-
- ####
- # Returns the number of Rows that were affected by the previous SQL command.
- ####
- sub RowCount{
- my($self, $Connection) = @_;
- if (! ref($self)){
- $Connection = $self;
- $self = 0;
- }
- if (! $Connection){$Connection = $self->{'connection'};}
- my(@Results) = ODBCRowCount($Connection);
- return (processError($self, @Results))[0];
- }
-
- ####
- # Returns the Statement Close Type -- how does ODBC Close a statment.
- # Types:
- # SQL_DROP
- # SQL_CLOSE
- # SQL_UNBIND
- # SQL_RESET_PARAMS
- ####
- sub GetStmtCloseType{
- my($self, $Connection) = @_;
- if (! ref($self)){
- $Connection = $self;
- $self = 0;
- }
- if (! $Connection){$Connection = $self->{'connection'};}
- my(@Results) = ODBCGetStmtCloseType($Connection);
- return (processError($self, @Results));
- }
-
- ####
- # Sets the Statement Close Type -- how does ODBC Close a statment.
- # Types:
- # SQL_DROP
- # SQL_CLOSE
- # SQL_UNBIND
- # SQL_RESET_PARAMS
- # Returns the newly set value.
- ####
- sub SetStmtCloseType{
- my($self, $Type, $Connection) = @_;
- if (! ref($self)){
- $Connection = $Type;
- $Type = $self;
- $self = 0;
- }
- if (! $Connection){$Connection = $self->{'connection'};}
- my(@Results) = ODBCSetStmtCloseType($Connection, $Type);
- return (processError($self, @Results))[0];
- }
-
- sub ColAttributes{
- my($self, $Type, @Field) = @_;
- my(%Results, @Results, $Results, $Attrib, $Connection, $Temp);
- if (! ref($self)){
- $Type = $Field;
- $Field = $self;
- $self = 0;
- }
- $Connection = $self->{'connection'};
- if (! scalar(@Field)){ @Field = $self->fieldnames;}
- foreach $Temp (@Field){
- @Results = ODBCColAttributes($Connection, $Temp, $Type);
- ($Attrib) = processError($self, @Results);
- if (wantarray){
- $Results{$Temp} = $Attrib;
- }else{
- $Results .= "$Temp";
- }
- }
- return wantarray? %Results:$Results;
- }
-
- sub GetInfo{
- my($self, $Type) = @_;
- my($Connection, @Results);
- if(! ref $self){
- $Type = $self;
- $self = 0;
- $Connection = 0;
- }else{
- $Connection = $self->{'connection'};
- }
- @Results = ODBCGetInfo($Connection, $Type);
- return (processError($self, @Results))[0];
- }
-
- sub GetConnectOption{
- my($self, $Type) = @_;
- my(@Results);
- if(! ref $self){
- $Type = $self;
- $self = 0;
- }
- @Results = ODBCGetConnectOption($self->{'connection'}, $Type);
- return (processError($self, @Results))[0];
- }
-
- sub SetConnectOption{
- my($self, $Type, $Value) = @_;
- if(! ref $self){
- $Value = $Type;
- $Type = $self;
- $self = 0;
- }
- my(@Results) = ODBCSetConnectOption($self->{'connection'}, $Type, $Value);
- return (processError($self, @Results))[0];
- }
-
-
- sub Transact{
- my($self, $Type) = @_;
- my(@Results);
- if(! ref $self){
- $Type = $self;
- $self = 0;
- }
- @Results = ODBCTransact($self->{'connection'}, $Type);
- return (processError($self, @Results))[0];
- }
-
-
- sub SetPos{
- my($self, @Results) = @_;
- @Results = ODBCSetPos($self->{'connection'}, @Results);
- $self->{'Dirty'} = 1;
- return (processError($self, @Results))[0];
- }
-
- sub ConfigDSN{
- my($self) = shift @_;
- my($Type, $Connection);
- if(! ref $self){
- $Type = $self;
- $Connection = 0;
- $self = 0;
- }else{
- $Type = shift @_;
- $Connection = $self->{'connection'};
- }
- my($Driver, @Attributes) = @_;
- @Results = ODBCConfigDSN($Connection, $Type, $Driver, @Attributes);
- return (processError($self, @Results))[0];
- }
-
-
- sub Version{
- my($self, @Packages) = @_;
- my($Temp, @Results);
- if (! ref($self)){
- push(@Packages, $self);
- }
- my($ExtName, $ExtVersion) = Info();
- if (! scalar(@Packages)){
- @Packages = ("ODBC.PM", "ODBC.PLL");
- }
- foreach $Temp (@Packages){
- if ($Temp =~ /pll/i){
- push(@Results, "ODBC.PM:$Win32::ODBC::Version");
- }elsif ($Temp =~ /pm/i){
- push(@Results, "ODBC.PLL:$ExtVersion");
- }
- }
- return @Results;
- }
-
-
- sub SetStmtOption{
- my($self, $Option, $Value) = @_;
- if(! ref $self){
- $Value = $Option;
- $Option = $self;
- $self = 0;
- }
- my(@Results) = ODBCSetStmtOption($self->{'connection'}, $Option, $Value);
- return (processError($self, @Results))[0];
- }
-
- sub GetStmtOption{
- my($self, $Type) = @_;
- if(! ref $self){
- $Type = $self;
- $self = 0;
- }
- my(@Results) = ODBCGetStmtOption($self->{'connection'}, $Type);
- return (processError($self, @Results))[0];
- }
-
- sub GetFunctions{
- my($self, @Results)=@_;
- @Results = ODBCGetFunctions($self->{'connection'}, @Results);
- return (processError($self, @Results));
- }
-
- sub DropCursor{
- my($self) = @_;
- my(@Results) = ODBCDropCursor($self->{'connection'});
- return (processError($self, @Results))[0];
- }
-
- sub SetCursorName{
- my($self, $Name) = @_;
- my(@Results) = ODBCSetCursorName($self->{'connection'}, $Name);
- return (processError($self, @Results))[0];
- }
-
- sub GetCursorName{
- my($self) = @_;
- my(@Results) = ODBCGetCursorName($self->{'connection'});
- return (processError($self, @Results))[0];
- }
-
- sub GetSQLState{
- my($self) = @_;
- my(@Results) = ODBCGetSQLState($self->{'connection'});
- return (processError($self, @Results))[0];
- }
-
-
- # ----------- R e s u l t P r o c e s s i n g F u n c t i o n s ----------
- ####
- # Generic processing of data into associative arrays
- ####
- sub updateResults{
- my ($self, $Error, @Results) = @_;
-
- undef %{$self->{'field'}};
-
- ClearError($self);
- if ($Error){
- SetError($self, $Results[0], $Results[1]);
- return ($Error);
- }
-
- @{$self->{'fnames'}} = @Results;
-
- foreach (0..$#{$self->{'fnames'}}){
- s/ +$//;
- $self->{'field'}->{${$self->{'fnames'}}[$_]} = $_;
- }
- return undef;
- }
-
- # ----------------------------------------------------------------------------
- # ----------------- D e b u g g i n g F u n c t i o n s --------------------
-
- sub Debug{
- my($self, $iDebug, $File) = @_;
- my(@Results);
- if (! ref($self)){
- if (defined $self){
- $File = $iDebug;
- $iDebug = $self;
- }
- $Connection = 0;
- $self = 0;
- }else{
- $Connection = $self->{'connection'};
- }
- push(@Results, ($Connection, $iDebug));
- push(@Results, $File) if ($File ne "");
- @Results = ODBCDebug(@Results);
- return (processError($self, @Results))[0];
- }
-
- ####
- # Prints out the current dataset (used mostly for testing)
- ####
- sub DumpData {
- my($self) = @_; my($f, $goo);
-
- # Changed by JOC 06-Apr-96
- # print "\nDumping Data for connection: $conn->{'connection'}\n";
- print "\nDumping Data for connection: $self->{'connection'}\n";
- print "Error: \"";
- print $self->Error();
- print "\"\n";
- if (! $self->Error()){
- foreach $f ($self->FieldNames){
- print $f . " ";
- $goo .= "-" x length($f);
- $goo .= " ";
- }
- print "\n$goo\n";
- while ($self->FetchRow()){
- foreach $f ($self->FieldNames){
- print $self->data($f) . " ";
- }
- print "\n";
- }
- }
- }
-
- sub DumpError{
- my($self) = @_;
- my($ErrNum, $ErrText, $ErrConn);
- my($Temp);
-
- print "\n---------- Error Report: ----------\n";
- if (ref $self){
- ($ErrNum, $ErrText, $ErrConn) = $self->Error();
- ($Temp = $self->GetDSN()) =~ s/.*DSN=(.*?);.*/$1/i;
- print "Errors for \"$Temp\" on connection " . $self->{'connection'} . ":\n";
- }else{
- ($ErrNum, $ErrText, $ErrConn) = Error();
- print "Errors for the package:\n";
- }
-
- print "Connection Number: $ErrConn\nError number: $ErrNum\nError message: \"$ErrText\"\n";
- print "-----------------------------------\n";
-
- }
-
- ####
- # Submit an SQL statement and print data about it (used mostly for testing)
- ####
- sub Run{
- my($self, $Sql) = @_;
-
- print "\nExcecuting connection $self->{'connection'}\nsql statement: \"$Sql\"\n";
- $self->sql($Sql);
- print "Error: \"";
- print $self->error;
- print "\"\n";
- print "--------------------\n\n";
- }
-
- # ----------------------------------------------------------------------------
- # ----------- E r r o r P r o c e s s i n g F u n c t i o n s ------------
-
- ####
- # Process Errors returned from a call to ODBCxxxx().
- # It is assumed that the Win32::ODBC function returned the following structure:
- # ($ErrorNumber, $ResultsText, ...)
- # $ErrorNumber....0 = No Error
- # >0 = Error Number
- # $ResultsText.....if no error then this is the first Results element.
- # if error then this is the error text.
- ####
- sub processError{
- my($self, $Error, @Results) = @_;
- if ($Error){
- SetError($self, $Results[0], $Results[1]);
- undef @Results;
- }
- return @Results;
- }
-
- ####
- # Return the last recorded error message
- ####
- sub error{
- return (Error(@_));
- }
-
- sub Error{
- my($self) = @_;
- if(ref($self)){
- if($self->{'ErrNum'}){
- my($State) = ODBCGetSQLState($self->{'connection'});
- return (wantarray)? ($self->{'ErrNum'}, $self->{'ErrText'}, $self->{'connection'}, $State) :"[$self->{'ErrNum'}] [$self->{'connection'}] [$State] \"$self->{'ErrText'}\"";
- }
- }elsif ($ErrNum){
- return (wantarray)? ($ErrNum, $ErrText, $ErrConn):"[$ErrNum] [$ErrConn] \"$ErrText\"";
- }
- return undef
- }
-
- ####
- # SetError:
- # Assume that if $self is not a reference then it is just a placeholder
- # and should be ignored.
- ####
- sub SetError{
- my($self, $Num, $Text, $Conn) = @_;
- if (ref $self){
- $self->{'ErrNum'} = $Num;
- $self->{'ErrText'} = $Text;
- $Conn = $self->{'connection'} if ! $Conn;
- }
- $ErrNum = $Num;
- $ErrText = $Text;
-
- ####
- # Test Section Begin
- ####
- # $! = ($Num, $Text);
- ####
- # Test Section End
- ####
-
- $ErrConn = $Conn;
- }
-
- sub ClearError{
- my($self, $Num, $Text) = @_;
- if (ref $self){
- undef $self->{'ErrNum'};
- undef $self->{'ErrText'};
- }else{
- undef $ErrConn;
- undef $ErrNum;
- undef $ErrText;
- }
- ODBCCleanError();
- return 1;
- }
-
-
- sub GetError{
- my($self, $Connection) = @_;
- my(@Results);
- if (! ref($self)){
- $Connection = $self;
- $self = 0;
- }else{
- if (! defined($Connection)){
- $Connection = $self->{'connection'};
- }
- }
-
- @Results = ODBCGetError($Connection);
- return @Results;
- }
-
-
-
-
- # ----------------------------------------------------------------------------
- # ------------------ A U T O L O A D F U N C T I O N -----------------------
-
- sub AUTOLOAD {
- # This AUTOLOAD is used to 'autoload' constants from the constant()
- # XS function. If a constant is not found then control is passed
- # to the AUTOLOAD in AutoLoader.
-
- my($constname);
- ($constname = $AUTOLOAD) =~ s/.*:://;
- #reset $! to zero to reset any current errors.
- $!=0;
- $val = constant($constname, @_ ? $_[0] : 0);
-
- if ($! != 0) {
- if ($! =~ /Invalid/) {
- $AutoLoader::AUTOLOAD = $AUTOLOAD;
- goto &AutoLoader::AUTOLOAD;
- }
- else {
-
- # Added by JOC 06-APR-96
- # $pack = 0;
- $pack = 0;
- ($pack,$file,$line) = caller;
- print "Your vendor has not defined Win32::ODBC macro $constname, used in $file at line $line.";
- }
- }
- eval "sub $AUTOLOAD { $val }";
- goto &$AUTOLOAD;
- }
-
-
- # --------------------------------------------------------------
- #
- #
- # Make sure that we shutdown ODBC and free memory even if we are
- # using perlis.dll on Win32 platform!
- END{
- # ODBCShutDown() unless $CacheConnection;
- }
-
-
- bootstrap Win32::ODBC;
-
- # Preloaded methods go here.
-
- # Autoload methods go after __END__, and are processed by the autosplit program.
-
- 1;
- __END__
-
-
-
-